<?php

namespace app\common\service\RpcService\RpcServer;

use app\Normal;
use app\common\service\ConsulService\ConsulClientProvider;
use app\model\BbcLogModel\RpcMicroServiceLog;

/**
 * RPC SWOOLE TCP SERVER
 * @author chenshubo
 */
class RpcSwooleTcpServer
{
    protected $service;  //服务器对象
    protected $serviceRootPath  = "\\app\\common\\service\\";
    protected $redisHashKey     = 'bbc_admin_rpc_socket';
    const SEND_BUFFER_LENGTH    = 1024;
    const DELIMITER_REPLACE     = "{{______\r\n\r\n______}}";//TCP 包体结束分隔符号
    public function start()
    {
        $this->service = new \Swoole\Server(env('RPC_BBC_ADMIN_SERVER.HOST'),env('RPC_BBC_ADMIN_SERVER.PORT'));
        $this->service->set([
            'worker_num'        => \swoole_cpu_num(),
            'task_worker_num'   => (int)env('RPC_BBC_ADMIN_SERVER.TASK_WORKER_NUM')    //配置此参数后将会启用task功能
        ]);
        $this->service->on('start',[$this,'onStart']);
        $this->service->on('connect',[$this,'onConnect']);
        $this->service->on('receive',[$this,'onReceive']);
        $this->service->on('task',[$this,'onTask']);  //使用task任务必须要注册task、finish回调函数
        $this->service->on('finish',[$this,'onFinish']);
        $this->service->on('close',[$this,'onClose']);
        $this->service->start();
    }
    
    /**
     * 建立连接
     * @param ObjectInfo $server
     * @param ObjectInfo $request
     */
    public function onStart($server)
    {
        //建立连接的时候开启服务注册
        if( env('CONSUL.CONSUL_STATUS')) {
            ConsulClientProvider::instance()->registerAgentTcpService('bbcAdminServices',env('RPC_BBC_ADMIN_SERVER.SERVER_IP'),env('RPC_BBC_ADMIN_SERVER.PORT'));
        }
    }
    
    /**
     * 建立连接
     * @param ObjectInfo $server
     */
    public function onConnect($server, $fd)
    {
        
    }
    
    /**
     * 收取客户端发包数据
     * @param ObjectInfo $server
     * @param ObjectInfo $frame
     */
    public function onReceive($server, $fd, $reactor_id, $context)
    {
        $data = [
            'data'  => base64_encode(rawurlencode($context)),
            'fd'    => $fd
        ];
        $server->task($data);
    }
    
    /**
     * 开启异步队列任务处理池功能
     * @param ObjectInfo $server
     * @param int $taskId
     * @param int $workId
     * @param mixed $data
     */
    public function onTask($server,$taskId,$workId,$data)
    {
        $start      = microtime(true);
        $parseBody  = @json_decode(rawurldecode(base64_decode($data['data'])),true);
        $fd         = $data['fd'];
        $modules    = $this->getParseModule($parseBody['method'], '.');
        try {
            $call_result = callApi($modules['class'], $modules['method'], $parseBody['argments'] ?? [] );
            $message = json_encode($call_result,JSON_UNESCAPED_UNICODE);
            //请求唯一标识
            $cacheKey = "{$this->redisHashKey}_{$parseBody['request_id']}_{$fd}";
            $redis = getRedisObj();
            $redis->Hset($this->redisHashKey,$cacheKey,$message);
            $response  = Normal::returnCode($call_result['code'],$call_result['msg'],[
                'cacheKey'  => $cacheKey,
                'cacheName' => $this->redisHashKey
            ]);
        } catch ( \Throwable $e ) {
            $response = Normal::returnCode(Normal::FAIL,"服务内部发生错误异常【{$e->getMessage()}】",[
                'errLines'  => $e->getLine(),
                'errFile'   => $e->getFile(),
            ]);
        }
        
        try {
            
            //获取客户端请求信息
            $clientInfo     = $server->getClientInfo($fd);
            $remote_ip      = $clientInfo['remote_ip']??'0.0.0.0';
            $server_port    = $clientInfo['server_port']??'0.0.0.0';
            
            //写入rpc 服务接口日志
            RpcMicroServiceLog::instance()->addLog([
                'interface_code'    => $parseBody['method'],
                'request_url'       => $remote_ip . ':' . $server_port ,
                'request_param'     => json_encode($parseBody['argments'],JSON_UNESCAPED_UNICODE),
                'response_code'     => $response['code'],
                'response_data'     => json_encode($response,JSON_UNESCAPED_UNICODE),
                'req_used_time'     => bcsub((string)microtime(true),(string)$start,3),
            ]);
        } catch ( \Throwable $e ) {
            $response = Normal::returnCode(Normal::FAIL,"服务内部发生错误异常日志处理失败【{$e->getMessage()}】",[
                'errLines'  => $e->getLine(),
                'errFile'   => $e->getFile(),
            ]);
        }
        
        $server->send($fd,json_encode($response,JSON_UNESCAPED_UNICODE).self::DELIMITER_REPLACE);
    }
    
    /**
     * 切割构造函数
     * @param string $string
     * @param string $delimeter
     */
    private function getParseModule( string $string ,string $delimeter)
    {
        $module = \explode($delimeter, $string);
        return [
            'class'     => $this->serviceRootPath.$module[0]."\\".$module[1],
            'method'    => $module[2],
        ];
    }
    
    /**
     * 队列处理完成回调方法
     * @param ObjectInfo $server
     * @param int $taskId
     * @param mixed $data
     */
    public function onFinish($server,$taskId,$data)
    {
    }
    
    /**
     * 连接关闭
     * @param ObjectInfo $server
     * @param int $fd
     */
    public function onClose($server,$fd)
    {
    }
}